package JVM.第02篇_类的加载篇;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *为什么要自定义类加载器?
 * ●隔离加载类
 *   在某些框架内进行中间件与应用的模块隔离，把类加载到不同的环境。
 *   比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。
 *   再比如: Tomcat这 类Web应用服务器，内部自定义了好几种类加载器，
 *    用于隔离同一个Web应用服务器上的不同应用程序。( 类的仲裁-->类冲突)
 * ● 修改类加载的方式
 * 类的加载模型并非强制，除Bootstrap外， 其他的加载并非一定要引入，
 * 或者根据实际情况在某个时间点进行按需进行动态加载
 * ● 扩展加载源
 * 比如从数据库、网络、甚至是电视机机顶盒进行加载
 * ● 防止源码泄湄
 * Java代码容易被编译和篡改，可以进行编译加密。
 * 那么类加载也需要自定义，还原加密的字节码。
 */
public class B6_自己实现自定义加载器 extends ClassLoader{
    private String rootPath;
    public B6_自己实现自定义加载器(String rootPath){
        this.rootPath = rootPath;
    }
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException{
        //转换为以文件路径表示的文件
        String filePath = classToFilePath(name);

        //获取指定路径的class文件对应的二进制流数据
        byte[] data = getBytesFromPath(filePath);

        //自定义 ClassLoader 内部调用;
        return defineClass(name,data,0,data.length);

    }
    private byte[] getBytesFromPath(String filePath){
        try (FileInputStream fis = new FileInputStream(filePath);
             ByteArrayOutputStream baos = new ByteArrayOutputStream();){
            byte[] buf = new byte[1024];
            int len;
            while ((len = fis.read(buf))!=-1){
                baos.write(buf,0,len);
            }
            return baos.toByteArray();

        }catch (IOException e){
            e.printStackTrace();
        }
        return null;

    }

    private String classToFilePath(String name){
        return rootPath + "\\" + name.replace(".","\\")
                + ".class";
    }

    public static void main(String[] args) {
        B6_自己实现自定义加载器 b1 =
                new B6_自己实现自定义加载器("C:\\Users\\Administrator\\IdeaProjects\\untitled\\src\\main\\java");

        B6_自己实现自定义加载器 b2 =
                new B6_自己实现自定义加载器("C:\\Users\\Administrator\\IdeaProjects\\untitled\\src\\main\\java");

        try {
            Class<?> aClass1 = b1.findClass("JVM.第02篇_类的加载篇.B6_CLassDemo");
            Class<?> aClass2 = b2.findClass("JVM.第02篇_类的加载篇.B6_CLassDemo");
            Constructor<?> declaredConstructor = aClass1.getDeclaredConstructor();//获取构造器
            Object o = declaredConstructor.newInstance();//获取类

            System.out.println(aClass1 == aClass2);//false
            System.out.println(aClass1);
            System.out.println(aClass1.getClassLoader());//B6_自己实现自定义加载器 加载器加载的
            Method xxx = aClass1.getMethod("XXX", String.class);//获取方法
            Method yyy = aClass1.getMethod("YYY", String.class);//获取方法
            Object x = xxx.invoke(o, "中国");//调用指定类的方法
            Object y = yyy.invoke(o, "中国");//调用指定类的方法
            System.out.println(x);
            System.out.println(y);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        }
    }
}
